package org.hawk.gwt.ppc.reflect.emul.java.lang.reflect;

import org.hawk.gwt.ppc.reflect.lang.ClassDelegate;

public final class Method extends AccessibleObject {
	private Object accessor;
	private ClassDelegate<?> clazz;
	private String signature;

	Method(Object accessor, ClassDelegate<?> clazz, String signature) {
		checkAccess(accessor);
		this.accessor = accessor;
		this.clazz = clazz;
		this.signature = signature;
	}

	@Override
	public int getModifiers() {
		return clazz.getModifiers(accessor,signature);
	}

	public ClassDelegate<?> getDeclaringClass(){
		return clazz;
	}
	
	public String getName(){
		int i = signature.indexOf('(');
		if(i<0)return signature;
		return signature.substring(0,i);
	}
	
	public ClassDelegate<?> getReturnType(){
		return ClassDelegate.forClass(
			clazz.getReturnType(accessor,signature));
	}
	
	public ClassDelegate<?>[] getParameterTypes(){
		return ClassDelegate.unmarshalSignature(signature);
	}
	
	public ClassDelegate<?>[] getExceptionTypes(){
		throw new UnsupportedOperationException();
	}
	
	public boolean equals(Object obj){
		if(obj==null)return false;
		if(obj==this)return true;
		if(obj instanceof Method){
			Method m = (Method)obj;
			return m.signature.equals(signature);
		}
		return false;
	}
	
	public int hashCode(){
		return signature.hashCode();
	}
	
	public String toString(){
		return signature;
	}
	
	public Object invoke(Object obj, Object...parameters) throws IllegalAccessException, 
			java.lang.reflect.InvocationTargetException{
		checkAccess();
		checkParameters(parameters,getParameterTypes());
		if(obj!=null&&!clazz.isInstance(obj)){
    		throw new IllegalArgumentException(
    			obj+" is not instance of "+clazz);
    	}
		try {
			return clazz.invoke(accessor,signature,obj,parameters);
		}
		catch(Throwable ex){
			throw new java.lang.reflect.InvocationTargetException(ex);
		}
	}
	
	public boolean isBridge(){
		return false;
	}
	
	public boolean isVarArgs(){
		throw new UnsupportedOperationException();
	}
	
	public boolean isSynthetic(){
		throw new UnsupportedOperationException();
	}
	
	public boolean getAnnotation(Class<?> cls){
		throw new UnsupportedOperationException();
	}
	
	public boolean getDeclaredAnnotations(){
		throw new UnsupportedOperationException();
	}
	
	public boolean getDefaultValue(){
		throw new UnsupportedOperationException();
	}
	
	public boolean getParameterAnnotations(){
		throw new UnsupportedOperationException();
	}
	
	static void checkParameters(Object[] parameters,
			ClassDelegate<?>[] parameterTypes) {
		if(parameters.length!=parameterTypes.length){
			throw new IllegalArgumentException(
					"Wrong number of parameters: expected "+
					parameterTypes.length+" but was "+parameters.length);
		}
		for(int i=0;i<parameters.length;i++){
			if(parameters[i]==null){
				if(parameterTypes[i].isPrimitive()){
					throw new IllegalArgumentException(
						"Unable to cast NULL to "+parameterTypes[i]);
				}
			}
			else if(!ClassDelegate.forClass(parameters[i].getClass())
					.isAssignableFrom(parameterTypes[i])){
				throw new IllegalArgumentException(
						"Unable to cast "+parameters[i].getClass()+
						" to "+parameterTypes[i]);
			}
			
		}
	}
}
